package com.freewater.springboot.demo.aop.pointcuts;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.aop.aspectj.annotation.AspectJProxyFactory;
import org.springframework.aop.support.AopUtils;
import org.springframework.util.ClassUtils;

/**
 * this
 * 格式说明
 * this(类型全限定名)：通过aop创建的代理对象的类型是否和this中指定的类型匹配；this中使用的表达式必须是类型全限定名，不支持通配符。
 * this(x)的匹配规则是：x.getClass.isAssingableFrom(proxy.getClass)
 * <p>
 * 链接：<a href="https://juejin.cn/post/7077425331954515999">Spring系列25：Spring AOP 切点详解</a>
 */
@Aspect
public class PointcutThis {
    interface I1 {
        void m();
    }

    static class C1 implements I1 {

        @Override
        public void m() {
            System.out.println("C1 m()");
        }
    }

    // 这里的this指的是proxy对象，限制proxy对象是I1类型或是其子类
    // 如果改为：@Pointcut("this(com.freewater.springboot.demo.aop.pointcuts.PointcutThis.C1)")，
    // 则切面不会执行，除非使用cglib代理（setProxyTargetClass(true)），让proxy对象成为C1的子类。
    @Pointcut("this(com.freewater.springboot.demo.aop.pointcuts.PointcutThis.I1)")
    public void pc() {
    }

    @Before("pc()")
    public void before(JoinPoint joinPoint) {
        System.out.println("before: " + joinPoint);
    }

    public static void main(String[] args) {
        I1 target = new C1();
        AspectJProxyFactory proxyFactory = new AspectJProxyFactory();
        proxyFactory.setTarget(target);
        //proxyFactory.setProxyTargetClass(true);
        // 获取C1上所有接口 spring工具类提供的方法
        Class<?>[] allInterfaces = ClassUtils.getAllInterfaces(target);
        // 设置代理接口
        proxyFactory.setInterfaces(allInterfaces);
        // 添加切面
        proxyFactory.addAspect(PointcutThis.class);
        // 获取代理
        I1 proxy = proxyFactory.getProxy();
        // 调用方法
        proxy.m();
        System.out.println("JDK代理? " + AopUtils.isJdkDynamicProxy(proxy));
        System.out.println("CGLIB代理? " + AopUtils.isCglibProxy(proxy));
        // 判断代理对象是否是I1类型的
        System.out.println(I1.class.isAssignableFrom(proxy.getClass()));
        // 判断代理对象是否是C1类型的
        System.out.println(C1.class.isAssignableFrom(proxy.getClass()));
    }
}
